package com.zonekey.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zonekey.entity.dto.OauthTokenReq;
import com.zonekey.entity.dto.Result;
import com.zonekey.utils.HttpKit;
import com.zonekey.utils.JsonUtil;
import com.zonekey.utils.PropertiesUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.ClientAlreadyExistsException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 登录，登出
 * @author: 靳俊英
 * @create: 2021-05-19 15:23
 **/
@RestController
@RequestMapping("auth")
public class LoginController {

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private RedisTokenStore redisTokenStore;
    @Autowired
    private JdbcClientDetailsService jdbcClientDetailsService;

    /**
     *
     * @param clientDetails
     * {"access_token_validity":7100,"additionalInformation":{},"authorities":[],
     * "authorized_grant_types":["password","refresh_token"],"autoapprove":["false"],
     * "client_id":"gg","client_secret":"test","refresh_token_validity":7100,
     * "redirect_uri":["www.baidu.com"],"resource_ids":[],"scope":["server"],"scoped":true,"secretRequired":true}
     * @return
     */
    @PostMapping("add/client")
    public Result addClient(@RequestBody BaseClientDetails clientDetails){
        jdbcClientDetailsService.addClientDetails(clientDetails);
        return Result.ok();
    }

    /**
     *  跟新密码
     * @param jsonObject
     * @return
     */
    @PostMapping("update/client/secret")
    public Result updateClient(@RequestBody JSONObject jsonObject){
        String clientId = jsonObject.getString("clientId");
        String clientSecret = jsonObject.getString("clientSecret");
        jdbcClientDetailsService.updateClientSecret(clientId,clientSecret);
        return Result.ok();
    }

    @PostMapping("remove/client")
    public Result removeClient(@RequestBody JSONObject jsonObject){
        String clientId = jsonObject.getString("clientId");
        jdbcClientDetailsService.removeClientDetails(clientId);
        return Result.ok();
    }

    /**
     *  添加默认的密码模式,对接空军
     * @param jsonObject
     * @return
     */
    @PostMapping("add/client/simple")
    public Result addClient(@RequestBody JSONObject jsonObject){
        String clientId = jsonObject.getString("clientId");
        String clientSecret = jsonObject.getString("clientSecret");
        if(StringUtils.isBlank(clientId) || StringUtils.isBlank(clientSecret)){
            return Result.fail().msg("clientId or clientSecret is empty");
        }
        BaseClientDetails baseClientDetails = new BaseClientDetails();
        baseClientDetails.setClientId(clientId);
        baseClientDetails.setClientSecret(clientSecret);
        baseClientDetails.setScope(Arrays.asList("server"));
        // 这两个类型，可以返回token和refresh_token
        baseClientDetails.setAuthorizedGrantTypes(Arrays.asList("password","refresh_token"));
        baseClientDetails.setAccessTokenValiditySeconds(7200);
        baseClientDetails.setRefreshTokenValiditySeconds(7200);
        try {
            jdbcClientDetailsService.addClientDetails(baseClientDetails);
        } catch (ClientAlreadyExistsException e) {
            return Result.fail().msg("当前clientId已存在");
        }
        return Result.ok();
    }


    /**
     *  oauth2.0 内置了一个获取token的请求 /oauth/token ;
     * @param request
     * @param oauthTokenReq
     * @return
     */
    @RequestMapping(value = "/oauth/token", method = {RequestMethod.POST, RequestMethod.GET})
    public JSONObject tokenLogout(HttpServletRequest request, OauthTokenReq oauthTokenReq) {
        logger.info("/auth/oauth/token >>> header Authorization = {},oauthTokenReq ={}",request.getHeader("Authorization"),JSON.toJSONString(oauthTokenReq));
        if (oauthTokenReq == null || StringUtils.isBlank(oauthTokenReq.getUsername())) {
            oauthTokenReq = JsonUtil.jsonToObject(request, OauthTokenReq.class);
        }
        if(oauthTokenReq == null || StringUtils.isBlank(oauthTokenReq.getUsername())
                || StringUtils.isBlank(oauthTokenReq.getPassword())){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code",1);
            jsonObject.put("msg","失败, missing client_id or client_secret");
            jsonObject.put("error","invalid_request");
            jsonObject.put("error_description","Missing parameters: username password");
            return jsonObject;
        }
        if(StringUtils.isBlank(request.getHeader("Authorization"))){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code",1);
            jsonObject.put("msg","失败, request header missing Authorization");
            return jsonObject;
        }
        String result = requestOauthToken(request.getHeader("Authorization"), oauthTokenReq);
        if(result == null){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code",1);
            jsonObject.put("msg","失败,用户信息授权未通过");
            return jsonObject;
        }
        return  JSON.parseObject(result);
    }

    @RequestMapping(value = "/oauth/2.0/token", method = {RequestMethod.POST, RequestMethod.GET})
    public JSONObject oauth2Token(HttpServletRequest request, OauthTokenReq oauthTokenReq) {
        logger.info("/auth/oauth/2.0/token >>> oauthTokenReq ={}",JSON.toJSONString(oauthTokenReq));
        if (oauthTokenReq == null || StringUtils.isBlank(oauthTokenReq.getUsername())) {
            oauthTokenReq = JsonUtil.jsonToObject(request, OauthTokenReq.class);
        }
        if(oauthTokenReq == null || StringUtils.isBlank(oauthTokenReq.getUsername()) || StringUtils.isBlank(oauthTokenReq.getPassword())){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("error","invalid_request");
            jsonObject.put("error_description","Missing parameters: username password");
            return jsonObject;
        }

        String clientId = oauthTokenReq.getClient_id();
        String clientSecret = oauthTokenReq.getClient_secret();
        if(StringUtils.isBlank(clientId) || StringUtils.isBlank(clientSecret)){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("error","invalid_request");
            jsonObject.put("error_description","Missing parameters: client_id client_secret");
            return jsonObject;
        }

        /**
         *  client_id 和client_secret的base64编码
         */
        byte[] b= new byte[0];
        try {
            b = (clientId+":"+clientSecret).getBytes("utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String str = new BASE64Encoder().encode(b);
        String authorization = "Basic "+str;

        String result = requestOauthToken(authorization, oauthTokenReq);
        if(result == null){
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code",1);
            jsonObject.put("msg","失败,用户信息授权未通过");
            jsonObject.put("error","authorized fail");
            jsonObject.put("error_description","authorization information is not match");
            return jsonObject;
        }
        return  JSON.parseObject(result);
    }

    @DeleteMapping("/logout")
    public Result tokenLogout(HttpServletRequest request){
        logger.info("/auth/token/logout >>> header Authorization ={}",request.getHeader("Authorization"));
        String bearAccessToken = request.getHeader("Authorization");
        String accessToken = bearAccessToken.substring("Bearer".length()).trim();
        redisTokenStore.removeAccessToken(accessToken);
        return  Result.ok().data(true);
    }

    @GetMapping("/test")
    //@PreAuthorize("hasAuthority('test')")
    public Result test(HttpServletRequest request){
        return  Result.ok();
    }

    @GetMapping("/test2")
    //@PreAuthorize("hasAuthority('admin')")
    public Result test2(HttpServletRequest request){
        return  Result.ok().data("test2");
    }


    /**
     *  构造/oauth/token 密码模式
     * @param authorization
     * @param oauthTokenReq
     * @return
     */
    private String requestOauthToken(String authorization,OauthTokenReq oauthTokenReq){
        Map<String, String> params = new HashMap<>();
        params.put("username", oauthTokenReq.getUsername());
        params.put("password",oauthTokenReq.getPassword());
        params.put("scope",oauthTokenReq.getScope());
        if(StringUtils.isNotBlank(oauthTokenReq.getGrant_type())){
            params.put("grant_type", oauthTokenReq.getGrant_type());
        }
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization",authorization);
        String contextPath="";
        if(PropertiesUtil.get("server.context-path") !=null && PropertiesUtil.get("server.context-path").replace("/","").length() >0){
            contextPath = PropertiesUtil.get("server.context-path");
        }else if(PropertiesUtil.get("server.servlet.context-path") !=null && PropertiesUtil.get("server.servlet.context-path").replace("/","").length() >0){
            contextPath = PropertiesUtil.get("server.servlet.context-path");
        }
        String url = "http://127.0.0.1:" + (PropertiesUtil.get("server.port"))+contextPath + "/oauth/token";
        String result ="";
        try {
            result = HttpKit.post(url, params, headers);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
